using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LS.UnityExtensions;

namespace LS.Digitize
{
    public class ListSerializer : CollectionSerializer
    {
        public ListSerializer(Type type, Type elementType) : base(type, elementType)
        {
        }

        public override void Write(IWriter writer, object obj)
        {
            ISerializer elementSerializer = ElementSerializer;
            if (obj == null)
            {
                writer.WriteNull();
                return;
            }

            var array = (IList)obj;

            if (elementSerializer == null)
                throw new ArgumentNullException("Serializer argument cannot be null.");

            for (int i = 0; i < array.Count; i++)
            {
                using (writer.WriteCollectionItemScope(i))
                {
                    elementSerializer.Write(writer, array[i]);
                }
            }
        }

        public override object Read(IReader reader)
        {
            ISerializer elementSerializer = ElementSerializer;
            IList instance = null;

            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (!collectionScope.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        return null;
                    }

                    instance = (IList)LSReflection.CreateInstance(elementSerializer.DataType);
                    while (true)
                    {
                        using (var itemScope = reader.ReadCollectionItemScope())
                        {
                            if (itemScope.ReadFailure)
                                break;
                            var item = elementSerializer.Read(reader);
                            if (item == null)
                                instance.Add(default);
                            else
                                instance.Add(item);
                        }
                    }
                }
            }

            return instance;
        }

        public override void ReadInto(IReader reader, ref object obj)
        {
            throw new NotImplementedException();
        }
    }

    public class ListSerializer<T> : CollectionSerializer<List<T>, T>
    {
        public ListSerializer() : base()
        {
        }

        public override void Write(IWriter writer, object obj)
        {
            ISerializer elementSerializer = ElementSerializer;
            if (obj == null)
            {
                writer.WriteNull();
                return;
            }

            ;

            var array = (IList)obj;

            if (elementSerializer == null)
                throw new ArgumentNullException("Serializer argument cannot be null.");

            using (writer.WriteCollectionScope())
            {
                for (int i = 0; i < array.Count; i++)
                {
                    using (writer.WriteCollectionItemScope(i))
                        elementSerializer.Write(writer, array[i]);
                }
            }
        }

        public override object Read(IReader reader)
        {
            ISerializer elementSerializer = ElementSerializer;
            IList instance = null;

            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (!collectionScope.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        return null;
                    }

                    instance = (IList)LSReflection.CreateInstance(elementSerializer.DataType);
                    while (true)
                    {
                        using (var itemScope = reader.ReadCollectionItemScope())
                        {
                            if (itemScope.ReadFailure)
                                break;
                            var obj = elementSerializer.Read(reader);
                            if (obj == null)
                                instance.Add(default);
                            else
                                instance.Add((T)obj);
                        }
                    }
                }
            }

            return instance;
        }

        public override void WriteData(IWriter writer, List<T> obj)
        {
            if (obj == null)
            {
                writer.WriteNull();
                return;
            }

            ;

            ISerializer elementSerializer = ElementSerializer;
            ISerializer<T> itemSerializer = elementSerializer as ISerializer<T>;
            var list = obj;

            using (writer.WriteCollectionScope())
            {
                if (itemSerializer != null)
                {
                    for (int i = 0; i < obj.Count; i++)
                    {
                        using (writer.WriteCollectionItemScope(i))
                            itemSerializer.WriteData(writer, obj[i]);
                    }
                }
                else
                {
                    for (int i = 0; i < obj.Count; i++)
                    {
                        using (writer.WriteCollectionItemScope(i))
                            elementSerializer.Write(writer, obj[i]);
                    }
                }
            }
        }

        public override List<T> ReadData(IReader reader)
        {
            ISerializer elementSerializer = ElementSerializer;
            ISerializer<T> itemSerializer = elementSerializer as ISerializer<T>;

            List<T> instance = null;
            using (var collectionScope = reader.ReadCollectionScope())
            {
                if (!collectionScope.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        return null;
                    }

                    instance = new List<T>();
                    if (itemSerializer != null)
                    {
                        while (true)
                        {
                            using (var itemScope = reader.ReadCollectionItemScope())
                            {
                                if (itemScope.ReadFailure)
                                    break;

                                var obj = itemSerializer.ReadData(reader);
                                if (obj == null)
                                    instance.Add(default);
                                else
                                    instance.Add(obj);
                            }
                        }
                    }
                    else
                    {
                        while (true)
                        {
                            using (var itemScope = reader.ReadCollectionItemScope())
                            {
                                if (itemScope.ReadFailure)
                                    break;

                                var obj = elementSerializer.Read(reader);
                                if (obj == null)
                                    instance.Add(default);
                                else
                                    instance.Add((T)obj);
                            }
                        }
                    }
                }
            }

            return instance;
        }

        public override void ReadInto(IReader reader, ref object obj)
        {
            throw new NotImplementedException();
        }
    }
}